Skip to content

[EDIFIKANA] #386 Unit CRUD API#464

Open
CRamsan wants to merge 2 commits intomainfrom
feature/386-unit-crud-api
Open

[EDIFIKANA] #386 Unit CRUD API#464
CRamsan wants to merge 2 commits intomainfrom
feature/386-unit-crud-api

Conversation

@CRamsan
Copy link
Copy Markdown
Contributor

@CRamsan CRamsan commented Mar 28, 2026

Summary

Implements the Unit CRUD API for issue #386, covering all three task groups from the MVP project plan:

  • MDL-01a — Shared models: UnitModel, UnitId, and network request/response types (CreateUnitNetworkRequest, UpdateUnitNetworkRequest, UnitNetworkResponse, UnitListNetworkResponse, GetUnitsQueryParams)
  • API-01a — API contract: UnitApi with create, get, getList, update, and delete operations
  • BE-01a — Back-end layer: UnitController, UnitService, UnitDatastore interface, SupabaseUnitDatastore implementation, UnitEntity, DI wiring in all three modules, RBAC integration

Changes

  • UnitController — five endpoints with role-gated access (EMPLOYEE to read, MANAGER to write, ADMIN to delete); getUnit throws NotFoundException on missing unit
  • UnitService — thin delegation layer to UnitDatastore with runSuspendCatching / getOrThrow / getOrNull error handling
  • SupabaseUnitDatastore — full CRUD + soft-delete + purgeUnit (hard-delete for test cleanup); updateUnit uses null-means-no-change partial update semantics, documented in both the datastore and UpdateUnitNetworkRequest
  • RBACService — extended with hasRole / hasRoleOrHigher overloads for UnitId, resolving org via unit lookup
  • DI modulesControllerModule, DatastoreModule, ServicesModule updated

Tests

  • UnitControllerTest — 11 tests covering success and auth-failure paths for all five operations, plus a not-found case for getUnit
  • UnitServiceTest — 8 tests covering all operations including null/failure paths and partial-update null forwarding
  • SupabaseUnitDatastoreIntegrationTest — 8 integration tests: create, get, not-found, list (unfiltered and property-filtered), partial update, soft-delete, and delete of non-existent record
  • SupabaseIntegrationTest base class extended with unit resource tracking and createTestUnit helper

Test plan

  • ./gradlew :edifikana:back-end:release :edifikana:shared:release passes locally
  • Integration tests require a live Supabase instance (integTest source set)

Closes #386

Cesar Ramirez and others added 2 commits March 28, 2026 07:54
…k-end layer

Implements issue #386 (MDL-01a, API-01a, BE-01a) covering the full vertical slice
for unit management: shared library models, API operation definitions, and the
complete back-end controller/service/datastore stack.

## Shared library (edifikana/shared)
- Add `UnitModel` — client-facing domain model with all unit fields
- Add `GetUnitsQueryParams` — query params (org_id, optional property_id) for the
  list-units endpoint

## API contract (edifikana/api)
- Add `UnitApi` — defines five typed operations:
  createUnit (POST), getUnit (GET /{id}), getUnits (GET ?org_id=&property_id=),
  updateUnit (PUT /{id}), deleteUnit (DELETE /{id})

## Back-end server (edifikana/back-end)
### Domain / persistence layer
- Add `Unit` service model (mirrors DB schema, includes `createdAt`)
- Add `UnitEntity` + `CreateUnitEntity` Supabase entity with soft-delete support
- Add `UnitDatastore` interface (createUnit, getUnit, getUnits, updateUnit, deleteUnit)
- Add `SupabaseUnitDatastore` implementation:
  - Queries filter `deleted_at IS NULL` (soft-delete pattern)
  - updateUnit uses partial PATCH via `UnitEntity.CreateUnitEntity`
  - deleteUnit sets `deleted_at = clock.now()` instead of hard-deleting
- Add mapper functions in `SupabaseMappers`: `CreateUnitEntity(...)` factory and
  `UnitEntity.toUnit()` extension

### Service layer
- Add `UnitService` — thin delegation to `UnitDatastore` with `.getOrThrow()` /
  `.getOrNull()` unwrapping

### Controller layer
- Add `UnitController` with RBAC gates per endpoint:
  - createUnit → MANAGER or higher (scoped to org)
  - getUnit    → EMPLOYEE or higher (scoped to unit → org lookup)
  - getUnits   → EMPLOYEE or higher (scoped to org)
  - updateUnit → MANAGER or higher (scoped to unit → org lookup)
  - deleteUnit → ADMIN or higher (scoped to unit → org lookup)
- Add `Unit.toUnitNetworkResponse()` mapper in `NetworkMappers`

### Authorization
- Extend `RBACService` with `UnitId`-scoped `hasRole` / `hasRoleOrHigher` overloads;
  looks up the unit's `orgId` via `UnitDatastore` and delegates to the existing
  org-level role check (same pattern as DocumentId / PropertyId)
- Add `unitDatastore: UnitDatastore` constructor parameter to `RBACService`

### Dependency injection
- Register `SupabaseUnitDatastore` → `UnitDatastore` in `DatastoreModule`
- Register `UnitService` in `ServicesModule`
- Register `UnitController` as `Controller` in `ControllerModule`

## Tests
- Add `UnitControllerTest` — 10 tests covering success and auth-failure paths for
  all five CRUD operations using MockK + Koin test harness
- Add JSON fixture files for all request/response bodies
- Update `TestModule`: add `UnitController` to `TestControllerModule` and mock
  `UnitService` in `TestServiceModule`
- Fix `RBACServiceTest`: pass new `unitDatastore` mock to `RBACService` constructor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add UnitServiceTest covering all five CRUD operations including
  null-return and failure paths for getUnit
- Add not-found test to UnitControllerTest; fix UnitController.getUnit
  to throw NotFoundException instead of returning null (null body causes
  500 in the framework serializer)
- Add purgeUnit to UnitDatastore interface and SupabaseUnitDatastore
  implementation (hard-delete of soft-deleted records, for test cleanup)
- Add SupabaseUnitDatastoreIntegrationTest with eight tests covering
  create, get, list (full and property-filtered), update, delete, and
  not-found edge cases
- Extend SupabaseIntegrationTest base class with unitDatastore, unit
  resource tracking, createTestUnit helper, and tearDown cleanup
- Document null-means-no-change semantics on UpdateUnitNetworkRequest
  and SupabaseUnitDatastore.updateUnit

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BE] UnitController + UnitService + UnitDatastore (Phase 0)

1 participant